import { booleanPointInPolygon, booleanPointOnLine, difference, lineIntersect, lineString, point, polygon } from "@turf/turf";
import { Face, Matrix3, Object3D, Plane, Scene, Vector2, Vector3 } from "three";
import MeshGeomLib from "./MeshGeomLib";

export default class VectorUtils {
    static assistVector3 = new Vector3();

    static segmentsIntr(a: Vector2, b: Vector2, c: Vector2, d: Vector2): Vector3 {

        // 三角形abc 面积的2倍  
        var area_abc = (a.x - c.x) * (b.y - c.y) - (a.y - c.y) * (b.x - c.x);

        // 三角形abd 面积的2倍  
        var area_abd = (a.x - d.x) * (b.y - d.y) - (a.y - d.y) * (b.x - d.x);

        // 面积符号相同则两点在线段同侧,不相交 (对点在线段上的情况,本例当作不相交处理);  
        // if ( area_abc*area_abd>=0 ) {  
        //     return false;  
        // }  

        // 三角形cda 面积的2倍  
        var area_cda = (c.x - a.x) * (d.y - a.y) - (c.y - a.y) * (d.x - a.x);
        // 三角形cdb 面积的2倍  
        // 注意: 这里有一个小优化.不需要再用公式计算面积,而是通过已知的三个面积加减得出.  
        // var area_cdb = area_cda + area_abc - area_abd ;  
        // if (  area_cda * area_cdb >= 0 ) {  
        //     return false;  
        // }  

        //计算交点坐标  
        var t = area_cda / (area_abd - area_abc);
        var dx = t * (b.x - a.x),
            dy = t * (b.y - a.y);
        return new Vector3(a.x + dx, 0, a.y + dy);

    }
    /** 用于临时变量的Vector3 可以减少new*/
    static assistVector3 = new Vector3();
    /** 用于临时变量的Vector2 可以减少new*/
    static assistVector2 = new Vector2();
    /** 上向量*/
    static readonly upVector3 = new Vector3(0, 1, 0);
    /** 右向量*/
    static readonly rightVector3 = new Vector3(1, 0, 0);
    /** 前向量*/
    static readonly frontVector3 = new Vector3(0, 0, 1);

    /**
     * 获取点离面数组最近的线的索引，线为 vec[idx] 和 vec[idx+1]
     * @param vec 
     * @param point 
     * @returns 
     */
    static getMinDistanceLine(vec: Vector3[], point: Vector3) {
        let disAry: number[] = [];
        let PP;
        for (let i = 0; i < vec.length; i++) {
            if (i === vec.length - 1) {
                PP = VectorUtils.getProjectVector(vec[i], vec[0], point);
            } else {
                PP = VectorUtils.getProjectVector(vec[i], vec[i + 1], point);
            }
            let dis = point.distanceTo(PP);
            disAry.push(dis);
        }
        // 最近的边
        let minIdx = disAry.indexOf(Math.min(...disAry));

        return minIdx;
    }

    /**
     * 获取直线方程
     * @param start 
     * @param end 
     * @returns 
     */
    static getLineFunc(start: Vector2, end: Vector2): number[] {

        let aV: number, bv: number, cV: number;
        if (start.x == end.x) {
            aV = 1;
            bv = 0;
            cV = -end.x;
        }
        else if (start.y == end.y) {
            aV = 0;
            bv = 1;
            cV = -end.y;
        }
        else {
            aV = end.y - start.y;
            bv = start.x - end.x;
            cV = end.x * start.y - start.x * end.y;
        }
        return [aV, bv, cV];
    }

    /**
     * 点到直线的距离
     * @param point 
     * @param start 
     * @param end 
     * @returns 
     */
    static pointDistanceToLine(point: Vector2, start: Vector2, end: Vector2): number {

        let lineFunc: number[] = VectorUtils.getLineFunc(start, end);
        let dis = (point.x * lineFunc[0] + point.y * lineFunc[1] + lineFunc[2]) / Math.sqrt(Math.pow(lineFunc[0], 2) + Math.pow(lineFunc[1], 2));
        return Math.abs(dis);
    }

    /**
     * 点是否在直线上
     * @param point 
     * @param start 
     * @param end 
     */
    static pointInLine(point: Vector2, start: Vector2, end: Vector2, errVal: number = 0.0001): boolean {
        let dis: number = VectorUtils.pointDistanceToLine(point, start, end);
        return dis < errVal;
    }

    /**
     * 点是否在线段上
     * @param point 
     * @param start 
     * @param end 
     * @param errVal 
     * @returns 
     */
    static pointInSegment(point: Vector2, start: Vector2, end: Vector2, errVal: number = 0.0001): boolean {
        let ps = point.sub(start).normalize();
        let pe = point.sub(end).normalize();

        let dot = ps.dot(pe);
        if (dot > errVal) {
            return false;
        }
        return VectorUtils.pointInLine(point, start, end);
    }

    static pointInSegment2(point: Vector2, start: Vector2, end: Vector2, errVal: number = 0.0001): boolean {
        let ps = point.sub(start);
        let pe = point.sub(end);

        let dot = ps.cross(pe);
        if (dot > errVal) {
            return false;
        }
        return true;
    }


    /**
     * 向量是否正交/垂直
     * @param a 
     * @param b 
     * @returns 
     */
    static isOrthogonal(a: Vector3, b: Vector3, err: number = 0.001): boolean {
        let aa = a.clone().normalize();
        let bb = b.clone().normalize();
        VectorUtils.rectifyVector(aa, err);
        VectorUtils.rectifyVector(bb, err);
        let dot = aa.x * bb.x + aa.y * bb.y + aa.z * bb.z;
        return Math.abs(dot) < 0.001;
    }

    /**
     * 向量是否平行
     * @param a 
     * @param b 
     */
    static isParallel(a: Vector3, b: Vector3): boolean {
        let aa = a.clone();
        let bb = b.clone();
        VectorUtils.rectifyVector(aa);
        VectorUtils.rectifyVector(bb);
        let an = aa.normalize();
        let bn = bb.normalize()
        let dot = an.dot(bn);
        return Math.abs(Math.abs(dot) - 1) < 0.000001;
    }

    /**
     * 向量是否同向
     * @param a 
     * @param b 
     */
    static isDirectionSame(a: Vector3, b: Vector3, error: number = Math.PI / 180 * 10): boolean {
        let aa = a.clone();
        let bb = b.clone();
        VectorUtils.rectifyVector(aa);
        VectorUtils.rectifyVector(bb);
        let angle = aa.clone().normalize().angleTo(bb.clone().normalize());
        return angle < error;
    }


    /**
     * 获取顶点的世界坐标
     * @param scene 
     * @param mesh 
     * @returns 
     */
    static getVertexWorldPosition(scene: Scene, mesh): Vector3[][] {

        scene.updateMatrixWorld(true);

        let planeVec = mesh.userData.planeVec;
        let posVec: any[] = [];
        for (let i = 0; i < planeVec.length; i++) {
            posVec[i] = [];
            for (let j = 0; j < planeVec[i].length; j++) {
                let vec3 = planeVec[i][j].clone();
                vec3.applyMatrix4(mesh.matrixWorld);
                vec3.sub(mesh.translateVec);
                posVec[i].push(vec3);
            }
        }
        return posVec;
    }

    static getSameFace(geoVec: Vector3[][], miny: number): number {

        for (let i = 0; i < geoVec.length; i++) {
            let istrue: boolean = true;
            let face = geoVec[i].concat();
            for (let j = 0; j < face.length; j++) {
                if (Math.abs(face[j].y - miny) > 1) {
                    istrue = false;
                    break;
                }
            }
            if (istrue) {
                return i;
            }
        }

        return -1;

    }

    static getSameFaceToX(geoVec: Vector3[][], minx: number): number {

        for (let i = 0; i < geoVec.length; i++) {
            let istrue: boolean = true;
            let face = geoVec[i].concat();
            for (let j = 0; j < face.length; j++) {
                if (Math.abs(face[j].x - minx) > 1) {
                    istrue = false;
                    break;
                }
            }
            if (istrue) {
                return i;
            }
        }

        return -1;

    }

    static getSameFaceToZ(geoVec: Vector3[][], minz: number): number {

        for (let i = 0; i < geoVec.length; i++) {
            let istrue: boolean = true;
            let face = geoVec[i].concat();
            for (let j = 0; j < face.length; j++) {
                if (Math.abs(face[j].z - minz) > 1) {
                    istrue = false;
                    break;
                }
            }
            if (istrue) {
                return i;
            }
        }

        return -1;

    }

    static getBottomFace(point: Vector3, mesh): Vector3[] {

        let geoVec = VectorUtils.getMeshWorldVertex(mesh);
        let ynormal = new Vector3(0, 1, 0);
        let face: Vector3[];
        let normal: Vector3, ldir: Vector3, wdir: Vector3;
        // let hfaces = [];
        let i: number, j: number;
        for (i = 0; i < geoVec.length; i++) {
            face = geoVec[i];
            ldir = face[1].clone().sub(face[0]);
            wdir = face[2].clone().sub(face[1]);
            normal = ldir.cross(wdir).normalize();
            // 向量平行，说明面是水平面,点在面上
            if (VectorUtils.isParallel(ynormal, normal) && Math.abs(face[0].y - point.y) < 0.01) {
                let isv = VectorUtils.IsClockWise(face);
                if (isv) face.reverse();
                return face;
            }
        }
        // let targetfaces = [];
        // let pos = new Vector2(point.x, point.z);
        // let polygon:Vector2[];
        // let pinpolygon:boolean;
        // for(i = 0; i< hfaces.length; i++){
        //     polygon =[];
        //     for(j = 0; j< hfaces[i].length;j++){
        //         polygon.push(new Vector2(hfaces[i][j].x, hfaces[i][j].z));
        //     }
        //     pinpolygon = VectorUtils.pointInPolygon(pos, polygon);
        //     if(pinpolygon){
        //         targetfaces.push([i,hfaces[i][0].y]);
        //     }
        // }
        return []
    }

    static getRabbetTopBottomFace(mesh): Vector3[] {
        let duan = mesh.userData.duan;
        let result = [];

        for (let i = 0; i < duan.length; i += 3) {
            result.push(mesh.localToWorld(new Vector3(duan[i], duan[i + 1], duan[i + 2])));
        }
        return result;
    }

    static getPasterBottomWorldPosition(mesh): Vector3[] {

        let result = [];
        let duan = mesh.userData.duan;
        for (let i = 0; i < 12; i += 3) {
            result.push(mesh.localToWorld(new Vector3(duan[i], duan[i + 1], duan[i + 2])));
        }

        return result
    }

    static getBottomVertexWorldPosition(mesh): Vector3[] {
        let geoVec = VectorUtils.getMeshWorldVertex(mesh);
        // let point: Vector3 = new Vector3();
        // let normal = new Vector3(0, -1, 0);
        let yvec: number[] = [];
        for (let i = 0; i < geoVec.length; i++) {
            let face = geoVec[i].concat();
            for (let j = 0; j < face.length; j++) {
                yvec.push(face[j].y);
            }
            // point.multiplyScalar(1 / face.length);
            // let inplane = VectorUtils.pointInVec(point, face, normal);
            // if (inplane) {
            // }
        }
        let miny = Math.min(...yvec);
        let index: number = VectorUtils.getSameFace(geoVec, miny);
        if (index !== -1) {
            let result = geoVec[index].concat();
            let isv = VectorUtils.IsClockWise(result);
            if (isv) result.reverse();
            return result;
        }
        return [];
    }

    static getBackVertexWorldPosition(mesh, isVertical: boolean = false): Vector3[] {
        let geoVec = VectorUtils.getMeshWorldVertex(mesh);

        let vec: number[] = [];

        for (let i = 0; i < geoVec.length; i++) {
            let face = geoVec[i].concat();
            for (let j = 0; j < face.length; j++) {
                if (isVertical) {
                    vec.push(face[j].x);
                }
                else {
                    vec.push(face[j].z);
                }
            }
            // point.multiplyScalar(1 / face.length);
            // let inplane = VectorUtils.pointInVec(point, face, normal);
            // if (inplane) {
            // }
        }
        let min = Math.min(...vec);
        let index: number;
        if (isVertical) {
            index = VectorUtils.getSameFaceToX(geoVec, min);
        }
        else {
            index = VectorUtils.getSameFaceToZ(geoVec, min);
        }
        if (index !== -1) {
            let result = geoVec[index].concat();
            let isv = VectorUtils.IsClockWise(result);
            if (isv) result.reverse();
            return result;
        }
        return [];
    }

    static getPointFaces(point: Vector3, target): Vector3 {
        let meshfaces = target.meshfaces;
        let face: Vector3[];
        let wp1: Vector3, wp2: Vector3, wp3: Vector3;
        let dir1: Vector3, dir2: Vector3, normal: Vector3;
        for (let i = 0; i < meshfaces.length; i++) {
            face = meshfaces[i];
            if (!face && face.length < 3) {
                console.log("模型的面数据出错:", target)
                continue;
            }
            if (!(face[0] instanceof Vector3) ||
                !(face[1] instanceof Vector3) ||
                !(face[2] instanceof Vector3)) {
                console.log("模型的面数据出错应该是Vector3,当前是:", face, target)
                continue;
            }
            wp1 = face[0].clone();
            wp2 = face[1].clone();
            wp3 = face[2].clone();

            dir1 = wp2.sub(wp1);
            dir2 = wp3.sub(wp1);
            normal = dir1.cross(dir2);

            let projp = point.clone().projectOnPlane(normal);

            projp.add(wp1);

            let tmpdir = point.clone().sub(projp);

            let dis = projp.distanceTo(point);

            if (dis < 0.1 || VectorUtils.isOrthogonal(tmpdir, normal)) {
                return normal;
            }
        }
        console.log("有没有检测到的点：", point);
        return null;
    }


    static getFaceByPoint(point: Vector3, mesh, normal: Vector3): Vector3[] {

        let faces = mesh.meshfaces;
        if (!faces || faces.length == 0) return [];

        let ps = point.clone();
        for (let i = 0; i < faces.length; i++) {
            let face = faces[i].concat();
            if (!face) {
                continue;
            }
            // 算点是不是在面上
            let inplane = VectorUtils.pointInVec(ps, face, normal);
            if (inplane) {
                return face;
            }
        }

        return [];
    }

    /**
     * 获取nodeshape 平面上的点
     * @param point 
     * @param mesh 
     * @param scene 
     * @param normal 
     * @returns 
     */
    static getPlaneVertexByPoint(point: Vector3, mesh, normal: Vector3): Vector3[] {

        let faces = VectorUtils.getMeshWorldVertex(mesh);

        let ps = point.clone();
        for (let i = 0; i < faces.length; i++) {
            let face = faces[i].concat();
            if (!face) {
                continue;
            }
            // 算点是不是在面上
            let inplane = VectorUtils.pointInVec(ps, face, normal);
            if (inplane) {
                return face;
            }
        }

        return [];
    }

    /**
     * 点是不是在平面内
     * @param point  点
     * @param vec 平面点集
     * @param normal 面法线
     * @returns 
     */
    static pointInVec(point: Vector3, vec: Vector3[], normal: Vector3) {
        // let dir = normal.clone();
        let plane = new Plane().setFromCoplanarPoints(vec[0], vec[1], vec[2]);
        if (VectorUtils.isParallel(plane.normal, normal)) {
            let dis = plane.distanceToPoint(point);
            if (Math.abs(dis) < 0.01) {
                return true;
            }
        }

        // for (let i = 0; i < vec.length; i++) {

        //     let tdir = point.clone().sub(vec[i]);
        //     let isOrth = VectorUtils.isOrthogonal(tdir, dir);
        //     if (!isOrth) {
        //         return false;
        //     }
        // }
        return false;
    }

    /**
     * 是否相同
     * @param v1 
     * @param v2 
     * @param err 
     * @returns 
     */
    static isSameVector(v1: Vector3, v2: Vector3, err: number = 0.000001) {

        if (Math.abs(v1.x - v2.x) < err && Math.abs(v1.y - v2.y) < err && Math.abs(v1.z - v2.z) < err) {
            return true;
        }

        return false;
    }

    private static _matrix3: Matrix3;

    static get matrix3(): Matrix3 {
        if (this._matrix3 === undefined) {
            this._matrix3 = new Matrix3();
        }
        return this._matrix3;
    }

    static getWorldNormalVec(normal: Vector3, object: Object3D) {
        let normalVector = new Vector3();

        object.updateMatrixWorld(true);
        VectorUtils.matrix3.getNormalMatrix(object.matrixWorld);

        normalVector.copy(normal).applyMatrix3(VectorUtils.matrix3).normalize();
        return normalVector;
    }

    /**
     * 获取面的世界法向量
     * @param face 
     * @param object 
     * @param normalVector 
     * @returns 
     */
    static getWorldNormal(face: Face, object: Object3D, normalVector?: Vector3): Vector3 {
        if (normalVector === null || normalVector === undefined) {
            normalVector = new Vector3();
        }

        object.updateMatrixWorld(true);
        VectorUtils.matrix3.getNormalMatrix(object.matrixWorld);

        normalVector.copy(face.normal).applyMatrix3(VectorUtils.matrix3).normalize();
        return normalVector;
    }

    static getLastNextIndex(index: number, vec: any[]): number[] {
        let lastP = index === 0 ? vec.length - 2 : index - 1;
        let nextP = index === (vec.length - 2) ? 1 : index + 2;
        return [lastP, nextP];
    }

    /**
     * 获取过 点point 垂直于 s-e 的点
     * @param start 
     * @param end 
     * @param point 
     * @returns 
     */
    static getProjectVector(start: Vector3, end: Vector3, point: Vector3): Vector3 {
        let AB = end.clone().sub(start);
        let AP = point.clone().sub(start);
        let AN = AP.projectOnVector(AB);
        let N = start.clone().add(AN);
        return N;
    }

    /**
     * 获取极值Y
     * @param ary vec3数组 
     * @type 例如 'x','y','z'
     * @param tag true 最小 false 最大
     * @returns null 或 min/max
     */
    static getExtremum(ary: Vector3[], type: string, isMin: boolean = true): number {
        if (!ary) return null;
        let extVec: number[] = [];
        for (let i = 0; i < ary.length; i++) {
            type == 'x' ? extVec.push(ary[i].x) :
                type == 'y' ? extVec.push(ary[i].y) :
                    type == 'z' ? extVec.push(ary[i].z) : null;
        }
        if (extVec.length === 0) return null;
        let miny = isMin ? Math.min(...extVec) : Math.max(...extVec);
        let index = extVec.indexOf(miny);
        return extVec[index];
    }

    /**
     * 获取点沿着法线方向到边的距离
     * @param origin 
     * @param normal 
     * @param vec 
     * @returns 
     */
    static getVerticalLineDistance(origin: Vector3, normal: Vector3, vec: Vector3[]): number {

        for (let i = 0; i < vec.length - 1; i++) {
            let start = vec[i];
            let end = vec[i + 1];
            let dir = normal.clone();
            // let isOrth = VectorUtils.isOrthogonal(normal, dir);

            // if (isOrth) {
            let pp = VectorUtils.getProjectVector(start, end, origin);
            let tag = pp.clone().sub(origin);
            let isSame = VectorUtils.isDirectionSame(tag, dir);
            if (isSame) {
                let dis = pp.clone().distanceTo(origin);
                return dis;
            }
            // }

        }

        return 0;
    }

    /**
    * 纠正0值，如果vector的x,y,z小于err则置为0
    * @param vec 
    * @param err 
    * @returns 
    */
    static rectifyVector(vec: Vector3, err: number = 0.000001): void {
        if (!vec) return;
        vec.x = Math.abs(parseFloat(vec.x.toFixed(4))) < err ? 0 : vec.x;
        vec.y = Math.abs(parseFloat(vec.y.toFixed(4))) < err ? 0 : vec.y;
        vec.z = Math.abs(parseFloat(vec.z.toFixed(4))) < err ? 0 : vec.z;
    }

    static ispointinpoly(p: Vector3, poly: Array<Vector3>, err: number = 0.01, isdraw: boolean = false) {
        if (!poly || poly.length < 3) { return false }
        let normal = new Vector3();
        let v1 = poly[0]; // 面的第一个顶点
        let v2 = poly[1]; // 面的第二个顶点
        let v3 = poly[2]; // 面的第三个顶点
        let oneDir: Vector3 = v1.clone().sub(v2);
        let twoDir: Vector3 = v3.clone().sub(v2);
        normal.crossVectors(v2.clone().sub(v1), v3.clone().sub(v1)).normalize();
        let dotProduct = normal.clone().dot(p.clone().sub(v1));
        // 偏移
        let offset: Vector3 = v2.clone().projectOnVector(normal);

        let p2 = p.clone().projectOnPlane(normal).add(offset);

        // let diss = p.clone().distanceTo(ddd[0]);
        // let ppp = this.rayInsertionsPoly([p,ddd[0]],poly,normal);

        // let r = this.getTriangleNormal(poly);
        if (isdraw) {
            // PothoLingDebugUtil.createPoints22([p2],0x000000);
            // PothoLingDebugUtil.createLines([p2,p],0xffffff);
        }
        let dis = p2.distanceTo(p);
        let boo = MeshGeomLib.pointinpoly(p2, poly, normal);
        return boo && dis < err;
    }

    /**
     * 一个点是否在平面上
     * @param point 点
     * @param poly 点集，数量需要大于3
     * @param err 误差值
     * @param normal 如果不为null，需要判断面的法向量是否与normal同向，同向才算在平面上
     * @returns 
     */
    static isOnPoly(point: Vector3, poly: Array<Vector3>, err: number = 0.01, normal?: Vector3) {
        if (!poly || poly.length < 3) { return false }
        let va = point.clone().sub(poly[0]);
        let v1 = poly[1].clone().sub(poly[0]);
        let v2 = poly[2].clone().sub(poly[0]);
        let n = v1.cross(v2);
        let dis = Math.abs(va.dot(n) / n.length());

        if (dis < err) {
            if (normal) {
                const dot = n.dot(normal);
                return dot > 0;
            }
            return true;
        } else {
            return false;
        }
    }

    /**
     * 点是否在平面内
     * @param point 
     * @param poly 
     * @param err 
     */
    static isInPoly(point: Vector3, poly: Array<Vector3>, err: number = 0.01) {
        if (!this.isOnPoly(point, poly, err)) return false;

        //是否在多边形内
        let pl = poly.length;
        let r = this.getTriangleNormal(poly);
        let r2 = r.clone().negate();
        for (let i: number = 0; i < poly.length; i++) {

            let j = (i + 1) % pl;
            let v0 = poly[i].clone().sub(point);
            let v1 = poly[j].clone().sub(poly[i]);
            let cn = v0.cross(v1).normalize();
            if (!(this.isEqual(r, cn, 0.05) || this.isEqual(r2, cn, 0.05))) {
                return false;
            }
        }

        return true;
    }





    /**
     * 获取面组的交线
     * @param poly 面组 Vector3[][]
     * @returns 
     */
    static getPolysInsertionsLine(poly: Array<Array<Vector3>>, nms: Vector3[]): Record<"points" | "segments", any> {
        if (!poly || poly.length <= 1) return null;

        let filters = poly.filter((face: Array<Vector3>) => {
            if (!face || face.length < 3) return false;
            return true;
        })

        if (filters.length <= 1) return null;

        let segments = [];
        let loopEnd = poly.length > 2 ? 0 : 1;

        for (let i: number = 0; i < poly.length - loopEnd; i++) {
            let j = (i + 1) % poly.length;
            let fa = poly[i];
            let fb = poly[j];
            let t = this.getPolyInsertionsLine(fa, fb, [nms[i], nms[j]]);
            if (t) segments.push(t)
        }

        //求交点
        let p = null;
        if (poly.length >= 3) {
            p = this.getThreePolyInsertionsPoint(poly, nms);
        }

        //交点和交线有什么关系 交点一点在交线中，一分为2 舍弃掉谁？？？？？  不舍弃，点优先就好

        return { points: p, segments: segments };
    }

    /**
     * 获取面与面的交线
     * @param polyA 
     * @param polyB 
     */
    static getPolyInsertionsLine(polyA: Array<Vector3>, polyB: Array<Vector3>, nms: Vector3[]): Vector3[] {

        //2个点 不管怎么算 这两个点都是确定的 交点一点在边上

        if (polyA.length < 3 || polyB.length < 3) return null;
        let points = [];
        for (let i: number = 0; i < polyA.length; i++) {
            let j = (i + 1) % polyA.length;
            let p = this.rayInsertionsPoly([polyA[i], polyA[j]], polyB, nms[1]);
            //还需要判断是否在面上
            if (p) {
                if (this.isInPoly(p, polyB)) {
                    points.push(p);
                }
            }
        }

        let pointsTwo = [];
        if (points.length < 2) {
            for (let ii: number = 0; ii < polyB.length; ii++) {
                let jj = (ii + 1) % polyB.length;
                let pp = this.rayInsertionsPoly([polyB[ii], polyB[jj]], polyA, nms[0]);
                if (pp) pointsTwo.push(pp);
            }
        }


        if (points.length == 2) return points;
        if (pointsTwo.length == 2) return pointsTwo;

        let arr = points.concat(pointsTwo);

        if (arr.length < 2) {
            return null;
        }

        return arr;
    }

    static XZarea(contour) {

        const n = contour.length;
        let a = 0.0;

        for (let p = n - 1, q = 0; q < n; p = q++) {

            a += contour[p].x * contour[q].z - contour[q].x * contour[p].z;

        }

        return a * 0.5;

    }

    static isXZClockWise(pts) {

        return VectorUtils.XZarea(pts) < 0;

    }

    static isCloseWise(poly: Vector3[]) {
        if (!poly || poly.length < 3) return false;

        let area = 0;
        area = this.area(poly[0], poly[1], poly[2]);
        return area < 0;

    }

    /** 三角形的有向面积*/
    static area(p: Vector3, q: Vector3, r: Vector3) {
        let f: Vector2 = new Vector2(p.x, p.y);
        let s: Vector2 = new Vector2(q.x, q.y);
        let t: Vector2 = new Vector2(r.x, r.y);
        if (p.y == q.y && p.y == r.y) {
            f.set(p.x, p.z);
            s.set(q.x, q.z);
            t.set(r.x, r.z);
        }
        else if (p.x == q.x && p.x == r.x) {
            f.set(p.y, p.z);
            s.set(q.y, q.z);
            t.set(r.y, r.z);
        }

        let num = (s.x - f.x) * (t.y - f.y) - (t.x - f.x) * (s.y - f.y);
        return num;
    }

    /**
     * 获取3个平面的交点
     * @param polys 
     */
    static getThreePolyInsertionsPoint(polys: Vector3[][], nms: Vector3[]) {
        if (polys.length < 3) return null;
        let polyA = polys[0];
        if (polyA.length < 3) return null;

        //需要推敲推敲 这里是有问题的...
        let nA = this.getTriangleNormal(polyA, true);
        let dA = polyA[0].clone().dot(nA);

        let polyB = polys[1];
        if (polyB.length < 3) return null;
        let nB = this.getTriangleNormal(polyB, true);
        let dB = polyB[0].clone().dot(nB);

        let polyC = polys[2];
        if (polyC.length < 3) return null;

        let nC = this.getTriangleNormal(polyC, true);
        let dC = polyC[0].clone().dot(nC);

        let ddd = nA.clone().cross(nB).dot(nC);
        if (ddd == 0) return null;

        let pa = nA.clone().cross(nB).multiplyScalar(dC);
        let pb = nB.clone().cross(nC).multiplyScalar(dA);
        let pc = nC.clone().cross(nA).multiplyScalar(dB);
        let p = pa.add(pb).add(pc).multiplyScalar(1 / ddd);
        return p;
    }


    /**
     * 获取射线与平面的交点
     * @param segment 线组 
     */
    static rayInsertionsPoly(ray: Vector3[], polys: Vector3[], polyNormal: Vector3) {
        if (ray.length < 2) return null;
        if (polys.length < 3) return null;


        let pn = polyNormal.clone();//this.getTriangleNormal(polys);
        let rn = ray[1].clone().sub(ray[0]).normalize();

        //p0+td pn=d => (p0+td).n = d; t = d - p0.n / d.n

        // if (pn.dot(rn) >= 0) {
        //     console.log("同向了");
        //     console.log(polys);
        //     console.log(ray);
        //     return null;

        // }

        let t = polys[0].clone().dot(pn) - ray[0].clone().dot(pn);
        t = t / pn.dot(rn);

        if (t < 0) {
            //??
        } else if (t >= 0) {
            if ((t * t) > ray[1].clone().sub(ray[0]).lengthSq()) {
                return null;
            }

            return ray[0].clone().add(ray[1].clone().sub(ray[0]).normalize().multiplyScalar(t));
        }



    }

    /**
     * 
     * @param poly 三角形的法向量
     * @returns 
     */
    static getTriangleNormal(poly: Array<Vector3>, checkWise: boolean = false): Vector3 {
        if (poly.length < 3) return new Vector3();

        let cp = [...poly];
        // if(checkWise){
        //     let isClock = this.IsClockWise(poly);
        //     if(!isClock)
        //     {
        //         cp = cp.reverse();
        //     }
        // }

        let l = cp[0].clone();
        let c = cp[1].clone();
        let r = cp[2].clone();


        let v1: Vector3 = c.clone().sub(l).normalize();
        let v2: Vector3 = r.clone().sub(c).normalize();
        let normal = v1.clone().cross(v2).normalize();
        return normal;
    }


    /**
     * 求过一个中心点，作给定长宽的矩形与面的四个交点
     * @param result 返回的数据
     * @param center 中心点 
     * @param poly 点集 数量>3
     * @param width 长
     * @param height 宽
     * by lixun
     * @returns 2022/4/27 这个没啥用，垃圾 --lixun
     */
    static getHoleVector3(result: Array<Vector3>, center: Vector3, poly: Vector3[], width: number, height: number): Array<Vector3> {
        // 就是斜墙这里的这里问题
        // if (!VectorUtils.isOnPoly(center, poly)) {
        //     return;
        // }

        let v1 = poly[0].clone();
        let v2 = poly[1].clone();
        let v3 = poly[2].clone();
        let v4 = poly[3].clone();

        let nx = v2.clone().sub(v1);
        let ny = v4.clone().sub(v1);

        let mx = nx.clone().normalize().multiplyScalar(width);
        let my = ny.clone().normalize().multiplyScalar(height);

        /* 洞的四个点坐标 */
        result.push(center.clone().sub(mx).sub(my));
        result.push(center.clone().add(mx).sub(my));
        result.push(center.clone().add(mx).add(my));
        result.push(center.clone().sub(mx).add(my));

        return result;
    }

    /**
     * 逆时针数组
     * @param points 
     * @returns 
     */
    static IsClockWise(points: any[]): boolean {
        if (points == null || points.length < 3) {
            return false;
        }

        // let num_points = points.length;
        let pts = [];
        for (let i = 0; i < points.length; i++) {
            pts.push(points[i].clone())
        }
        pts.push(points[0].clone());


        const n = pts.length;
        let a = 0.0;

        for (let p = n - 1, q = 0; q < n; p = q++) {

            a += pts[p].x * pts[q].z - pts[q].x * pts[p].z;

        }

        return a * 0.5 < 0;

    }

    /**
     * 获取mesh的世界坐标点
     * @param scene 
     * @param mesh 
     * @param vec 
     * @returns 
     */
    static getMeshWorldVertex(mesh): Vector3[][] {
        let faceVec: Vector3[][] = [];
        let vec: Vector3[][] = mesh.meshfaces;
        if (!vec || vec.length <= 0) return faceVec;
        let vec3: Vector3;
        let i: number, j: number;
        for (i = 0; i < vec.length; i++) {
            faceVec[i] = [];
            for (j = 0; j < vec[i].length; j++) {
                vec3 = vec[i][j].clone();
                faceVec[i].push(mesh.localToWorld(vec3));
            }
        }
        return faceVec;
    }

    /**
    * 判断点所在的线
    * @param point 点
    * @param faces 面点集
    * @param err 误差值
    * @returns 0 点 1 线 2 面 -1 不在几何上
    * return [0, [pointsData[index]]];返回一个点或者一条线
    */
    static pointInFacesLins(pos: Vector3, faces: any[], err: number = 10): any[] {
        let vertex: Vector3;
        let proj: Vector3;
        let tmpface: Vector3[];

        let i: number, j: number;
        let face: any[];
        let ps = pos.clone();
        let dis: number;

        let pointsData = [];
        let pointsDis = [];
        let linesData = [];
        let linesDis = [];
        for (i = 0; i < faces.length; i++) {
            face = faces[i].concat();

            if (!face) continue;

            tmpface = face.concat(face[0]);
            for (j = 0; j < tmpface.length - 1; j++) {

                // 先算下点是否与面上的点重合
                vertex = tmpface[j];
                if (!vertex) continue;
                dis = ps.distanceTo(vertex);
                if (dis < err) {

                    pointsData.push(vertex);
                    pointsDis.push(dis);
                }

                // 算点是否与线重合
                proj = VectorUtils.getProjectVector(tmpface[j], tmpface[j + 1], ps);
                dis = ps.distanceTo(proj);

                if (dis < err) {

                    linesData.push([tmpface[j + 1], tmpface[j]]);
                    linesDis.push(dis);
                }
            }
        }

        let index = -1;
        if (pointsDis.length > 0) {
            let minpoint = Math.min(...pointsDis);
            index = pointsDis.indexOf(minpoint);
            return [0, [pointsData[index]]];
        }

        if (linesDis.length > 0) {
            let minline = Math.min(...linesDis);
            index = linesDis.indexOf(minline);
            return [1, linesData[index]];
        }

        return [-1, []];
    }

    /**
     * 判断点所在的点、线、面
     * 判断点在点0上，还是线1上，还是面2上
     * @param point 点
     * @param faces 面点集
     * @param err 误差值
     * @returns 0 点 1 线 2 面 -1 不在几何上
     * return [0/1,一个点/一根线]
     */
    static pointInFaces(pos: Vector3, faces: any[], err: number = 10): any[] {
        // let vertex: Vector3;
        // let proj: Vector3;
        let tmpface: Vector3[];
        let inplane: boolean;
        let i: number, j: number;
        let face: any[];
        let ps = pos.clone();
        // let dis: number;

        let result = VectorUtils.pointInFacesLins(pos, faces, err);
        // 如果在点和线上，就立刻返回res
        if (result[0] !== -1) {
            return result;
        }
        // 如果不在点也不在线上，那就遍历所有面，如果在面上，就返回res[2,其中一个面]
        for (i = 0; i < faces.length; i++) {
            face = faces[i].concat();
            if (!face) continue;
            tmpface = face.concat(face[0]);
            for (j = 0; j < tmpface.length - 1; j++) {
                // 算点是不是在面上
                inplane = VectorUtils.isOnPoly(ps, face, err);
                if (inplane) {
                    return [2, face];
                }
            }
        }
        return [-1, []];
    }

    static isDisEqual(a: Vector3, b: Vector3, err: number = 0.1): boolean {
        let dis = a.distanceTo(b);
        return dis < err;
    }

    /**
     * 两个点是否相等
     * @param a 
     * @param b 
     * @param err 
     * @returns 
     */
    static isEqual(a: Vector3, b: Vector3, err: number = 0.01): boolean {
        return Math.abs(a.x - b.x) < err && Math.abs(a.y - b.y) < err && Math.abs(a.z - b.z) < err;
    }

    /**
     * 点映射到平面上的点
     * @param points 需要映射的点集
     * @param polygon 必须是个按顺序的多边形
     */
    static pointProjectOnPlane(points: Vector3[], polygon: Vector3[]): Vector3[] {
        let one: Vector3 = polygon[0];
        let two: Vector3 = polygon[1];
        let three: Vector3 = polygon[2];

        let oneDir: Vector3 = one.clone().sub(two);
        let twoDir: Vector3 = three.clone().sub(two);
        // 面法线
        let normal: Vector3 = oneDir.clone().cross(twoDir).normalize();
        // 偏移
        let offset: Vector3 = two.clone().projectOnVector(normal);

        let result: Vector3[] = [];
        for (let point of points) {
            result.push(point.clone().projectOnPlane(normal).add(offset));
        }
        return result;
    }

    static mergeLines(duanAry: number[][], min: number, max: number, err: number = 0.1): number[][] {
        let ary = [[min, max]];
        let sp: number, ep: number;
        let ms: number, me: number;
        for (let i = 0; i < duanAry.length; i++) {
            sp = duanAry[i][0];
            ep = duanAry[i][1];

            for (let j = 0; j < ary.length; j++) {
                if (ary[j].length != 2) continue;
                ms = ary[j][0];
                me = ary[j][1];

                if (ms - sp > err && ep - ms > err && me - ep > err) {
                    ary[j] = [ep, me];
                }
                else if (Math.abs(ms - sp) < err && ep - ms > err && me - ep > err) {
                    ary[j] = [ep, me];
                }
                else if (sp - ms > err && me - ep > err) {
                    ary[j] = [ms, sp];
                    ary.push([ep, me]);

                }
                else if (sp - ms > err && Math.abs(me - ep) < err) {
                    ary[j] = [ms, sp];
                }
                else if (sp - ms > err && me - sp > err && ep - me > err) {
                    ary[j] = [ms, sp];
                }
            }


        }



        return ary;
    }

    static getDifXY(hlpath: Vector2[], gmpath: Vector2[], min: number, max: number, isv: boolean = false) {
        // 算一下,相交切断
        let xAry = [];
        let duanAry = [];
        let sp: number, ep: number;
        let intersects = VectorUtils.getPolyDifference(hlpath, gmpath);
        if (intersects && intersects.geometry.coordinates.length > 0) {
            console.log(intersects)
            for (let s = 0; s < intersects.geometry.coordinates.length; s++) {
                if (intersects.geometry.coordinates.length === 1) {
                    xAry = [];
                    for (let z = 0; z < intersects.geometry.coordinates[s].length; z++) {
                        isv ? xAry.push(intersects.geometry.coordinates[s][z][1]) :
                            xAry.push(intersects.geometry.coordinates[s][z][0]);

                    }
                    xAry = [...new Set(xAry)];

                    sp = Math.min(...xAry);
                    ep = Math.max(...xAry);

                    duanAry.push([sp, ep]);
                }
                else {
                    for (let z = 0; z < intersects.geometry.coordinates[s].length; z++) {
                        xAry = [];

                        for (let q = 0; q < intersects.geometry.coordinates[s][z].length; q++) {
                            isv ? xAry.push(intersects.geometry.coordinates[s][z][q][1]) :
                                xAry.push(intersects.geometry.coordinates[s][z][q][0]);
                        }
                        xAry = [...new Set(xAry)];

                        sp = Math.min(...xAry);
                        ep = Math.max(...xAry);

                        duanAry.push([sp, ep]);
                    }
                }
            }
        }

        let result = VectorUtils.mergeLines(duanAry, min, max);
        return result;

    }

    static getPolyDifference(poly1: Vector3[] | Vector2[], poly2: Vector3[] | Vector2[]) {
        let i: number;
        let points1: any[] = [];
        for (i = 0; i < poly1.length; i++) {
            points1.push([poly1[i].x, poly1[i].y]);
        }
        points1.push([poly1[0].x, poly1[0].y]);
        let polygon1 = polygon([points1]);

        let points2: any[] = [];
        for (i = 0; i < poly2.length; i++) {
            points2.push([poly2[i].x, poly2[i].y]);
        }
        points2.push([poly2[0].x, poly2[0].y]);
        let polygon2 = polygon([points2]);

        let intersects = difference(polygon1, polygon2);
        return intersects;
    }

    /**
     * 获取线段的交点
     * @param path 
     * @param polygon 
     * @returns 
     */
    static getLineIntersect(path: Vector3[] | Vector2[], polygon: Vector3[] | Vector2[]) {

        let line1: any, line2: any;
        let points: any[] = [];
        for (let i = 0; i < path.length; i++) {
            points.push([path[i].x, path[i].y]);
        }
        line1 = lineString(points);

        let posV: number[][] = [];
        for (let j = 0; j < polygon.length; j++) {
            posV.push([polygon[j].x, polygon[j].y]);
        }
        posV.push([polygon[0].x, polygon[0].y])
        line2 = lineString(posV)

        let intersects = lineIntersect(line1, line2);
        return intersects;
    }



    static changePointPoly(po: Vector3 | Vector2, polyg: Vector3[] | Vector2[]): any[] {
        let pt = point([po.x, po.y]);
        let posV: number[][] = [];
        for (let j = 0; j < polyg.length; j++) {
            posV.push([polyg[j].x, polyg[j].y]);
        }
        posV.push([polyg[0].x, polyg[0].y])

        return [pt, posV];
    }

    static posInLine(po: Vector3 | Vector2, polyg: Vector3[] | Vector2[]): boolean {

        let cpp = VectorUtils.changePointPoly(po, polyg);
        let pt = cpp[0];
        let posV: number[][] = cpp[1];

        let line = lineString([...posV]);
        return booleanPointOnLine(pt, line);
    }

    static posInPolygon(po: Vector3 | Vector2, polyg: Vector3[] | Vector2[]): boolean {
        let cpp = VectorUtils.changePointPoly(po, polyg);
        let pt = cpp[0];
        let posV: number[][] = cpp[1];

        let poly = polygon([posV]);
        return booleanPointInPolygon(pt, poly);
    }

    /**
     * 点是否在多边形内(包括在线上)
     * @param po 
     * @param polyg 
     * @returns 
     */
    static pointInPolygonsLine(po: Vector3 | Vector2, polyg: Vector3[] | Vector2[]): boolean {
        let cpp = VectorUtils.changePointPoly(po, polyg);
        let pt = cpp[0];
        let posV: number[][] = cpp[1];

        let poly = polygon([posV]);
        let line = lineString([...posV]);
        let ponline = booleanPointOnLine(pt, line);
        let pinpolygon = booleanPointInPolygon(pt, poly);
        return pinpolygon || ponline;
    }


    /**
     * 点是否在多边形内(不包括线)
     * @param po 
     * @param polyg 
     * @returns 
     */
    static pointInPolygon(po: Vector3 | Vector2, polyg: Vector3[] | Vector2[]): boolean {
        let cpp = VectorUtils.changePointPoly(po, polyg);
        let pt = cpp[0];
        let posV: number[][] = cpp[1];

        let poly = polygon([posV]);
        let line = lineString([...posV]);
        let ponline = booleanPointOnLine(pt, line);
        let pinpolygon = booleanPointInPolygon(pt, poly);
        return pinpolygon && !ponline;
    }

    /**
     * 点在图形的第几条边上
     * @param po 
     * @param polyg 
     * @returns 
     */
    static pointInLineIndex(po: number[], polyg: Vector3[] | Vector2[]): number {
        let pt = point([parseInt(po[0].toFixed(0)), parseInt(po[1].toFixed(0))]);
        let line: any;
        for (let j = 0; j < polyg.length - 1; j++) {
            line = lineString([[parseInt(polyg[j].x.toFixed(0)), parseInt(polyg[j].y.toFixed(0))], [parseInt(polyg[j + 1].x.toFixed(0)), parseInt(polyg[j + 1].y.toFixed(0))]]);
            if (booleanPointOnLine(pt, line)) {
                if (Math.abs(po[0] - polyg[j + 1].x) < 0.01 && Math.abs(po[1] - polyg[j + 1].y) < 0.01) {
                    return j + 1;
                }
                return j;
            }
        }
        let endidx = polyg.length - 1;
        line = lineString([[parseInt(polyg[endidx].x.toFixed(0)), parseInt(polyg[endidx].y.toFixed(0))], [parseInt(polyg[0].x.toFixed(0)), parseInt(polyg[0].y.toFixed(0))]]);
        if (booleanPointOnLine(pt, line)) {
            if (Math.abs(po[0] - polyg[0].x) < 0.01 && Math.abs(po[1] - polyg[0].y) < 0.01) {
                return 0;
            }
            return endidx;
        }

        return -1;
    }

    static fiterZero(data: number[]): number[] {
        for (let i = 0; i < data.length; i++) {
            if (Math.abs(data[i]) < 0.00001) {
                data[i] = 0;
            }
        }
        return data;
    }


    /** */
    static distancePointToSegments(v: Vector3, segments: Vector3[]) {

        if (!v || !segments || segments.length < 0) return 0;

        let sa = new Vector3();
        let ba = new Vector3();

        sa.subVectors(v, segments[0]);
        ba.subVectors(segments[1], segments[0])

        sa.projectOnVector(ba).add(segments[0]);

        // let sl = sa.lengthSq();
        // let dis = sl * Math.sqrt(1 - Math.pow(sa.normalize().dot(ba.normalize()), 2));
        return v.distanceTo(sa);
    }

    static getLineVectorPath(paths: number[]): Vector2[][] {
        let result = [];

        let newpath = [...paths, paths[0], paths[1]]
        for (let i = 0; i < newpath.length - 2; i += 2) {
            result.push([new Vector2(newpath[i], newpath[i + 1]), new Vector2(newpath[i + 2], newpath[i + 3])]);
        }
        return result;
    }

    /**
     * 数组转为世界vector3
     * @param points xyz排列的数组
     * @param mold 
     * @returns 
     */
    static numberToWorldVector3(points: number[], mold): Vector3[] {
        let tmp: Vector3;
        let result: Vector3[] = [];
        for (let i = 0; i < points.length; i += 3) {
            tmp = mold.localToWorld(new Vector3(points[i], points[i + 1], points[i + 2]));
            result.push(tmp)
        }
        return result;
    }

    /**
     * 数组转为vector3
     * @param points xyz排列的数组
     * @returns 
     */
    static numberToVector3(points: number[]): Vector3[] {
        let result: Vector3[] = [];
        for (let i = 0; i < points.length; i += 3) {
            result.push(new Vector3(points[i], points[i + 1], points[i + 2]))
        }
        return result;
    }

    /**
     * 数组转为vector2
     * @param points xy排列的数组
     * @returns 
     */
    static numberToVector2(points: number[]): Vector2[] {
        let result: Vector2[] = [];
        for (let i = 0; i < points.length; i += 2) {
            result.push(new Vector2(points[i], points[i + 1]))
        }
        return result;
    }

    /**
     * 获取点组的最值 x y 
     * @param points 
     * @returns 
     */
    static getVectorMinMax(points: number[]) {
        let xv: number[] = [];
        let yv: number[] = [];
        for (let i = 0; i < points.length; i += 2) {
            xv.push(points[i]);
            yv.push(points[i + 1]);
        }
        return {
            minx: Math.min(...xv),
            maxx: Math.max(...xv),
            miny: Math.min(...yv),
            maxy: Math.max(...yv)
        };
    }

    static pointsInPolygon(path: Vector2[], poly: Vector2[]): boolean {

        for (let po of path) {
            if (!VectorUtils.pointInPolygon(po, poly)) {
                return false;
            }
        }

        return true;
    }




    /**
     * 判断线与线之间的相交
     * @param p1 直线1上一点
     * @param v1 直线1方向
     * @param p2 直线2上一点
     * @param v2 直线2方向
     * @returns 交点 不存在为null
     */
    static LineLineIntersection(p1: Vector3, v1: Vector3, p2: Vector3, v2: Vector3): Vector3 {
        let intersection = null;
        if (v1.clone().dot(v2) == 1) {
            return intersection;
            // 两线平行
        }

        let startPointSeg: Vector3 = p2.clone().sub(p1);
        let vecS1: Vector3 = v1.clone().cross(v2);            // 有向面积1
        let vecS2: Vector3 = startPointSeg.clone().cross(v2); // 有向面积2
        let num = startPointSeg.clone().dot(vecS1);

        // 判断两这直线是否共面
        // if (Math.abs(num) <= 0.001){
        //     return intersection;
        // }

        // 有向面积比值，利用点乘是因为结果可能是正数或者负数
        let num2 = vecS2.clone().dot(vecS1) / vecS1.lengthSq();

        intersection = p1.clone().add(v1.clone().multiplyScalar(num2));
        return intersection;
    }

    static pointCmp(a: Vector3, b: Vector3, center: Vector3): boolean {

        let oax = a.x - center.x;
        let oay = a.y - center.y;
        let obx = b.x - center.x;
        let oby = b.y - center.y;

        let atan2a = -Math.atan2(oay, -oax);
        let atan2b = -Math.atan2(oby, -obx);

        return atan2a > atan2b;
    }

    /**
     * 逆时针数组
     * @param vec 
     */
    static clockwiseSortPoints(vec: Vector3[]) {
        let center: Vector3 = new Vector3();

        let x = 0, y = 0;
        let i = 0, j = 0;
        for (i = 0; i < vec.length; i++) {
            x += vec[i].x;
            y += vec[i].y;
        }

        center.x = x / vec.length;
        center.y = y / vec.length;

        let tmp: Vector3;
        for (i = 0; i < vec.length - 1; i++) {
            for (j = 0; j < vec.length - i - 1; j++) {
                if (!VectorUtils.pointCmp(vec[j], vec[j + 1], center)) {
                    tmp = vec[j].clone();
                    vec[j] = vec[j + 1].clone();
                    vec[j + 1] = tmp;
                }
            }
        }
    }


    static getPolygonArea(poly1: any[]): number {
        let i: number;
        let points1: Vector2[] = [];
        for (i = 0; i < poly1.length - 1; i++) {
            points1.push(new Vector2(poly1[i][0], poly1[i][1]));
        }
        return VectorUtils.getArea(points1);
    }

    static getArea(ps: any[]): number {
        let n = ps.length;
        let A = 0;
        for (let p = n - 1, q = 0; q < n; p = q++) {
            let pval = ps[p];
            let qval = ps[q];
            A += pval.x * qval.y - qval.x * pval.y;
        }
        return (A * 0.5);
    }


    static getVectorAngle(v1: Vector3, v2: Vector3, fv: Vector3): number {
        let ag = v1.angleTo(v2);
        let cross = fv.clone().cross(ConstDef.ynormal).normalize();
        // let cross = v1.clone().cross(v2).normalize();
        let v3 = v1.clone().applyAxisAngle(cross, ag);
        if (VectorUtils.isDirectionSame(v3, v2)) {
            return ag;
        }
        else {
            return -ag;
        }
    }

    static findSecondMax(arr: number[]) {
        let max = arr[0]
        let max2 = -1
        for (let i = 1; i < arr.length; i++) {
            if (arr[i] > max) {
                max2 = max
                max = arr[i]
            } else {
                max2 = arr[i] > max2 ? arr[i] : max2
            }
        }
        console.log(max + ' ' + max2)
        return max;
    }
}